﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading;

namespace SqlBlocks.Gui
{
    public delegate void FileFound(SqlFileManager.Message message, EventArgs e);
    public class SqlFileManager : IDisposable
    {
        public event FileFound Found;

        FileSystemWatcher watcher = null;
        public SqlFileManager(string path)
        {
            watcher = new FileSystemWatcher(path);
            watcher.IncludeSubdirectories = true;
            watcher.Filter = "*.sql";

            watcher.Created += watcher_Event;
            watcher.Changed += watcher_Event;

            watcher.EnableRaisingEvents = true;

        }

        enum FileNameType
        {
            Named = 1,
            Down = 2,
            Up = 3
        }

        static Dictionary<FileNameType, ProcessFileDelegate> typeHandlers;

        delegate Message ProcessFileDelegate(FileInfo file);
        static SqlFileManager()
        {
            typeHandlers = new Dictionary<FileNameType, ProcessFileDelegate>();
            typeHandlers.Add(FileNameType.Named, ProcessNamedFile);
            typeHandlers.Add(FileNameType.Up, ProcessUpFile);
            typeHandlers.Add(FileNameType.Down, ProcessDownFile);
        }

        static Message ProcessNamedFile(FileInfo f)
        {
            lock (fileLock)
            {
                if (!f.Exists) return null;
                return new Message
                {
                    File = f,
                    Description = "Skipping named migration"
                };
            }
        }

        static object fileLock = (object)true;

        static Message ProcessUpFile(FileInfo f)
        {
            lock (fileLock)
            {
                if (!f.Exists) return null;
                var last = FileHelper.GetLastVersion(f.DirectoryName);
                f.MoveTo(Path.Combine(f.DirectoryName, string.Format("{0:##000}_{1}", last + 1, f.Name)));
            }
            return new Message
            {
                File = f,
                Description = "Creating a partial new migration\r\nStill need a \"down\" description"
            };
        }

        static Message ProcessDownFile(FileInfo f)
        {
            lock (fileLock)
            {
                if (!f.Exists) return null;
                var incomplete = FileHelper.GetMigrations(f.Directory.FullName).Incomplete;

                if (incomplete.Count == 0)
                {
                    return new Message
                    {
                        File = f,
                        Description = "Can't find an incomplete migration to match the down file with"
                    };
                }

                var desc = incomplete[0];

                foreach (var f1 in incomplete)
                {
                    if (f1.Version < desc.Version)
                    {
                        desc = f1;
                    }
                }

                f.MoveTo(((RawSqlMigration)desc.Migration).DownFile);

                return new Message
                {
                    File = f,
                    Description = string.Format("Down migration found for \"{0}\"", desc.Name)
                };
            }
        }

        void watcher_Event(object sender, FileSystemEventArgs e)
        {
            FileInfo f = new FileInfo(e.FullPath);

            var type = GetFileType(f.Name);

            if (Found != null && f.Exists && typeHandlers.ContainsKey(type))
            {
                var m = typeHandlers[type](f);
                if (m != null)
                {
                    Found(m, new EventArgs());
                }
            }
        }

        FileNameType GetFileType(string filename)
        {
            if (FileHelper.IsUp(filename) || FileHelper.IsDown(filename))
            {
                return FileNameType.Named;
            }

            if ("down".Equals(filename, StringComparison.CurrentCultureIgnoreCase) ||
                "down.sql".Equals(filename, StringComparison.CurrentCultureIgnoreCase))
            {
                return FileNameType.Down;
            }

            return FileNameType.Up;
        }

        public override string ToString()
        {
            return watcher.Path;
        }

        #region IDisposable Members

        public void Dispose()
        {
            watcher.Dispose();
        }

        #endregion

        public class Message
        {
            public string Description { get; set; }
            public FileInfo File { get; set; }
        }
    }
}
